/*----------------------------------------------------------------------------------------
*
*  Copyright 2019, Gao Hai Hui, <fromzeropoint@126.com>.  All rights reserved.
*  https://gitee.com/helloworldghh/xoskit.git
*  Use of this source code is governed by a MIT license
*  that can be found in the License file.
*
----------------------------------------------------------------------------------------*/
#include "../import/head.h"
#include "../data_struct/head.h"
#include "../macro/head.h"
#include "odbc_recordset.h"
#include "odbc_desc.h"
#include "odbc_stmt.h"
#include "odbc_connect.h"
#include "mgr.h"

namespace xos_odbc
{
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //

    static odbc_connect::ITEM_DATA xos_item_data;

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //

    odbc_connect::odbc_connect()
    {
        init_data();
    }

    odbc_connect::~odbc_connect()
    {
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // public method

    int odbc_connect::get_xos_item_data( ITEM_DATA *& item_ptr )
    {
        int ret = 0;
        xos_item_data.set_container( mgr::container() );
        item_ptr = &xos_item_data;
        return ret;
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // i_odbc_node method

    // 
    // 利用不同数据库的功能生成对应sql放在lpszSql中，得到最后新增记录的自增ID。
    // 成功返回0,结果在lpIntId中
    // 需要在设置异步前执行
    // 
    int odbc_connect::get_last_insert_id( const char * lpszSql, int * lpIntId )
    {
        int ret = 0;

        intptr_t StrLen_or_IndPtr = sizeof( int32_t );
        xos_odbc::i_stmt * pStmt = 0;
        int32_t nRetId = -1;

        if( 0 == ret )
        {
            ret = create_stmt( (i_stmt **)&pStmt );
            if( 0 == ret )
            {
                ret = pStmt->execute_direct( lpszSql );
            }
        }

        if( 0 == ret )
        {
            int nColNum = 0;
            ret = pStmt->get_col_num( nColNum );
            if( ( 0 != ret ) || ( 1 != nColNum ) )
            {
                ret = 1;
            }
        }

        if( 0 == ret )
        {
            odbc_stmt::T * pT = (odbc_stmt::T *)pStmt;
            ret = pT->bind_colum( 0, xos_odbc::i_stmt::VT_I32, &nRetId, sizeof( nRetId ), &StrLen_or_IndPtr );
        }

        if( 0 == ret )
        {
            int r = pStmt->fetch();
            if( 0 == r )
            {
                if( lpIntId )
                {
                    *lpIntId = nRetId;
                }
            }
            else
            {
                ret = 1;
            }
        }

        if( pStmt )
        {
            pStmt->release();
            pStmt = 0;
        }

        return ret;
    }

    // 
    // 生成i_recordset
    // 
    i_recordset * odbc_connect::recordset( i_recordset ** ppv )
    {
        odbc_recordset::T * pF = 0;
        xos_stl::init_pool_item( pF );
        pF->set_connect( this );
        pF->prepare_stmt();
        if( ppv )
        {
            *ppv = pF;
        }
        return pF;
    }

    // 
    //  0 : success. 
    //  1 : running. 
    // -1 : failed
    // 
    int odbc_connect::end_trans( bool bCommit )
    {
        int ret = 0;

        if( 0 == ret )
        {
            SQLRETURN r = SQLEndTran( SQL_HANDLE_DBC, m_hDbc, bCommit ? SQL_COMMIT : SQL_ROLLBACK );
            ret = m_err.connect_ret( m_hDbc, r );
        }

        // 如果返回成功或失败，非pending状态，都是结束，改回直接提交模式
        if( 1 != ret )
        {
            SQLRETURN r = SQLSetConnectAttrW( m_hDbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, 0 );
            m_err.connect_ret( m_hDbc, r );
        }

        return ret;
    }

    // 
    //  0 : success. 
    //  1 : running. 
    // -1 : failed
    // 
    int odbc_connect::begin_trans()
    {
        int ret = 0;

        if( 0 == ret )
        {
            SQLRETURN r = SQLSetConnectAttrW( m_hDbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OFF, 0 );
            ret = m_err.connect_ret( m_hDbc, r );
        }

        return ret;
    }

    // 
    //  0 : success. 
    //  1 : running. 
    // -1 : failed
    // 
    int odbc_connect::connect()
    {
        using namespace std;
        int ret = 0;
        
        xos_box::i_string * pStr = mgr::container()->box()->string();
        xos_box::i_buf * pOutputBuf = mgr::container()->box()->buf();
        xos_box::i_buf * pInputBuf = mgr::container()->box()->buf();

        if( 0 == ret )
        {
            pStr->set( m_con_str.c_str() );
            int r = mgr::container()->misc()->utf8_to_utf16( pStr, pInputBuf );
            if( 0 != r )
            {
                ret = 1;
            }
        }

        if( 0 == ret )
        {
            SQLSMALLINT out_len = (SQLSMALLINT)( pOutputBuf->get_buf_size( 0 ) / sizeof( wchar_t ) );
            SQLWCHAR * lpszOut = (SQLWCHAR *)pOutputBuf->get_data( 0, 0, 0, 0 );
            SQLWCHAR * lpszInStr = (SQLWCHAR *)pInputBuf->get_data( 0, 0, 0, 0 );
            SQLSMALLINT in_len = (SQLSMALLINT)pInputBuf->get_len( 0 );
            SQLSMALLINT real_len = 0;
            SQLRETURN r = SQLDriverConnectW( m_hDbc, m_hWnd, lpszInStr, in_len, lpszOut, out_len, &real_len, SQL_DRIVER_COMPLETE_REQUIRED );
            ret = m_err.connect_ret( m_hDbc, r );
            if( 0 == ret )
            {
                pOutputBuf->set_len( real_len * sizeof( int16_t ) );
                mgr::container()->misc()->utf16_to_utf8( pOutputBuf, pStr );
                m_out_con_str = pStr->get( 0, 0 );
            }
        }

        if( 0 != ret )
        {
            LOG4( "------------------------odbc connect failed = %s", m_err.get_info( 0, 0, 0, 0 ) );
        }

        xos_stl::release_interface( pOutputBuf );
        xos_stl::release_interface( pInputBuf );
        xos_stl::release_interface( pStr );
        
        return ret;
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //

    int odbc_connect::set_connect_info( void * hWnd, const char * lpszConStr )
    {
        int ret = 0;
        m_hWnd = (SQLHWND)hWnd;
        m_con_str = lpszConStr;
        return ret;
    }

    // 
    // 创建一个htmt
    // 
    int odbc_connect::create_stmt( i_stmt ** ppv )
    {
        int ret = 0;

        SQLHANDLE hStmt = SQL_NULL_HSTMT;

        if( ( 0 == ret ) && ( SQL_NULL_HDBC == m_hDbc ) )
        {
            ret = 1;
        }

        if( 0 == ret )
        {
            SQLRETURN r = SQLAllocHandle( SQL_HANDLE_STMT, m_hDbc, &hStmt );
            ret = m_err.stmt_ret( hStmt, r );
        }

        if( 0 == ret )
        {
            SQLRETURN r = SQLSetStmtAttrW( hStmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0 );
            ret = m_err.stmt_ret( hStmt, r );
        }

        if( ( 0 == ret ) && ppv )
        {
            odbc_stmt::T * p = odbc_stmt::get_item_from_pool();
            p->set_handle( hStmt );
            p->init();
            *ppv = p;
            hStmt = SQL_NULL_HSTMT;
        }

        if( SQL_NULL_HSTMT != hStmt )
        {
            SQLFreeHandle( SQL_HANDLE_STMT, hStmt );
            hStmt = SQL_NULL_HSTMT;
        }

        return ret;
    }

    int odbc_connect::set_handle( SQLHANDLE hDbc )
    {
        int ret = 0;
        m_hDbc = hDbc;
        return ret;
    }

    int odbc_connect::init()
    {
        int ret = 0;
        return ret;
    }

    int odbc_connect::term()
    {
        int ret = 0;

        if( SQL_NULL_HDBC != m_hDbc )
        {
            SQLDisconnect( m_hDbc );
            SQLFreeHandle( SQL_HANDLE_DBC, m_hDbc );
            m_hDbc = SQL_NULL_HDBC;
        }

        init_data();

        return ret;
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int odbc_connect::init_data()
    {
        int ret = 0;
        m_hDbc = SQL_NULL_HDBC;
        m_out_con_str = "";
        m_err.clear();
        m_con_str = "";
        m_hWnd = 0;
        return ret;
    }

} // xos_odbc
